/*
 /	This file is part of Answerkeys Disabler
 /
 /	Answerkeys Disabler is free software: you can redistribute it and/or modify
 /	it under the terms of the GNU General Public License as published by
 /	the Free Software Foundation, either version 3 of the License, or
 /	(at your option) any later version.
 /
 /	Answerkeys Disabler is distributed in the hope that it will be useful,
 /	but WITHOUT ANY WARRANTY; without even the implied warranty of
 /	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /	GNU General Public License for more details.
 /
 /	You should have received a copy of the GNU General Public License
 /	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /
 /	Copyright 2008 Daan van Dijk (drvdijk@gmail.com)
 */

#include "stdafx.h"
#include "TAPIDevice.h"
#include "touchpanel.h"

#define EXIT_EVENT_NAME _T("AnswerkeysDisabler_Exit_Event")

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{

	HANDLE g_hExitEvent = CreateEvent(NULL, FALSE, FALSE, EXIT_EVENT_NAME);

	// Unload option
	if(0 == _tcscmp(_T("/u"), lpCmdLine)) {
		SetEvent(g_hExitEvent);
		CloseHandle(g_hExitEvent);
		return 0;
	}

	// Check if the program is already running
	bool bIsRunning = false;
	if(GetLastError() == ERROR_ALREADY_EXISTS)
	{
		bIsRunning = true;
	}
	if(bIsRunning)
	{
		if(IDYES == MessageBox(NULL, L"Answerkeys Disabler is already enabled. Disable it?", L"Answerkeys Disabler", MB_YESNO | MB_ICONQUESTION))
		{
			SetEvent(g_hExitEvent);
		}
		CloseHandle(g_hExitEvent);
		return 0;
	}

	// Initialize TAPI device
	CTAPIDevice m_cDevice;
	if(!m_cDevice.Initialize(NULL) || !m_cDevice.OpenLine())
	{
		MessageBox(GetForegroundWindow(), L"Sorry, Answerkeys Disabler failed initialization", L"Error", MB_OK | MB_ICONERROR);
		CloseHandle(g_hExitEvent);
		return 1;
	}

	// Get a touch panel handle, create hiding events
	CTouchPanel m_cTouchPanel;
	if (!m_cTouchPanel.isLoaded()) {
		MessageBox(GetForegroundWindow(), L"Sorry, Answerkeys Disabler failed initialization", L"Error", MB_OK | MB_ICONERROR);
		CloseHandle(g_hExitEvent);
		return 1;
	}
	HANDLE g_hEnableHidingEvent = CreateEvent(NULL, FALSE, FALSE, ENABLE_HIDING_EVENT_NAME);
	HANDLE g_hStopHidingEvent = CreateEvent(NULL, FALSE, FALSE, STOP_HIDING_EVENT_NAME);
	bool currentlyLocking = false;

	// Start listening for power state changes
	MSGQUEUEOPTIONS msgQpm = {0}; 
	msgQpm.dwSize = sizeof(MSGQUEUEOPTIONS);
	msgQpm.dwFlags = MSGQUEUE_NOPRECOMMIT;
	msgQpm.dwMaxMessages = 0;
	msgQpm.cbMaxMessage = sizeof(POWER_BROADCAST_POWER_INFO) + sizeof(POWER_BROADCAST) + MAX_PATH;
	msgQpm.bReadAccess = TRUE;
	HANDLE g_hPMNotifyQ= CreateMsgQueue(_T("PowerMsgQ"), &msgQpm);
	HANDLE hPwrNotify = RequestPowerNotifications(g_hPMNotifyQ, PBT_TRANSITION); 
	DWORD lastPowerOn = GetTickCount();
	bool aSleep = false;

	// Get registry changed handle to catch stylus changes
	HKEY m_hRegistryKey;
	HANDLE g_hStylusChanged = NULL;
	if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _T("ControlPanel\\Keybd"), 0, 0, &m_hRegistryKey)) {
		g_hStylusChanged = CeFindFirstRegChange(m_hRegistryKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET);
		if (g_hStylusChanged != INVALID_HANDLE_VALUE) {
			CeFindNextRegChange(g_hStylusChanged);
		}
	}
	RegCloseKey(m_hRegistryKey);

	// Pack the waiting events together for the loop
	HANDLE arrEvents[5] = {g_hExitEvent, g_hEnableHidingEvent, g_hStopHidingEvent, g_hPMNotifyQ, g_hStylusChanged};

	bool doContinue = true;
	bool disableScreenAfterEnablingTouchScreen = false;

	while (doContinue) {
		DWORD dwWait;
		dwWait = WaitForMultipleObjects(5, arrEvents, FALSE, currentlyLocking ? 100 : INFINITE);
		if (dwWait == WAIT_OBJECT_0) { // g_hExitEvent
			doContinue = false;
			m_cTouchPanel.SetDisabled(false);
		}
		else if (dwWait == WAIT_OBJECT_0 + 1) { // g_hEnableHidingEvent
			bool justOn = (GetTickCount() - lastPowerOn < 1000);
			if (justOn) {
				m_cTouchPanel.SetDisabled(true);
				disableScreenAfterEnablingTouchScreen = justOn || aSleep;
				currentlyLocking = true;
			}
		}
		else if (dwWait == WAIT_OBJECT_0 + 2) { // g_hStopHidingEvent
			m_cTouchPanel.SetDisabled(false);
			DWORD tickDifference = GetTickCount() - lastPowerOn > 1000;
			if (!currentlyLocking && tickDifference) {
				disableScreenAfterEnablingTouchScreen = false;
			}
			else if (tickDifference < 1000) {
				disableScreenAfterEnablingTouchScreen = true;
			}
			currentlyLocking = false;
			if (disableScreenAfterEnablingTouchScreen) {
				aSleep = true;
				keybd_event(VK_OFF, 0, 0, 0);
				keybd_event(VK_OFF, 0, KEYEVENTF_KEYUP, 0);
			}
		}
		else if (dwWait == WAIT_OBJECT_0 + 3) { // g_hPMNotifyQ
			BYTE  buffer[sizeof(POWER_BROADCAST) + sizeof(POWER_BROADCAST_POWER_INFO)];
			DWORD dwRead;
			DWORD dwFlags;
			if(ReadMsgQueue(g_hPMNotifyQ, (LPVOID)buffer, sizeof(buffer), &dwRead, INFINITE, &dwFlags)) {
				POWER_BROADCAST* pPower = (POWER_BROADCAST*)buffer;
				if (pPower->Message == PBT_TRANSITION) {
					std::wstring powerState = pPower->SystemPowerState;
					if (powerState == L"unattended") {
						aSleep = true;
					}
					else if (powerState == L"on") {
						if (aSleep) lastPowerOn = GetTickCount();
						aSleep = false;
					}
					else if (powerState == L"backlightoff") {
						aSleep = false;
					}
				}
			}
		}
		else if (dwWait == WAIT_OBJECT_0 + 4) { // g_hStylusChanged
			currentlyLocking = false;
			disableScreenAfterEnablingTouchScreen = false;
			m_cTouchPanel.SetDisabled(false);
			CeFindNextRegChange(g_hStylusChanged);
		}
		else if (dwWait == WAIT_TIMEOUT) {
			m_cTouchPanel.SetDisabled(true);
		}
	}

	// Clean up
	StopPowerNotifications(hPwrNotify);
	CloseMsgQueue(g_hPMNotifyQ);
	CloseHandle(g_hStopHidingEvent);
	CloseHandle(g_hEnableHidingEvent);
	// m_cTouchPanel
	m_cDevice.Shutdown();
	CloseHandle(g_hExitEvent);

	return 0;
}
